1 module hip.util.to_string_range; 2 import std.range.interfaces; 3 import std.range.primitives; 4 import std.traits:isArray; 5 import std.typecons:isTuple; 6 7 void put(Sink, E)(ref Sink sink, E e) 8 { 9 static if(is(E == U[], U)) 10 { 11 static if(__traits(hasMember, sink, "preAllocate")) 12 sink.preAllocate(e.length); 13 foreach(element; e) 14 sink.put(element); 15 } 16 else 17 sink.put(e); 18 } 19 20 21 void toStringRange(Sink, Enum)(ref Sink sink, Enum enumMember) if(is(Enum == enum)) 22 { 23 foreach(mem; __traits(allMembers, Enum)) 24 if(__traits(getMember, Enum, mem) == enumMember) 25 { 26 put(sink, Enum.stringof ~ "." ~ mem); //@nogc string, resolved at compile time 27 return; 28 } 29 put(sink, Enum.stringof ~ ".|MEMBER_NOT_FOUND|"); //@nogc string, resolved at compile time 30 } 31 32 void toStringRange(Sink)(ref Sink sink, float f) 33 if(isOutputRange!(Sink, char)) 34 { 35 if(f != f) //nan 36 return put(sink, "nan"); 37 else if(f == -float.infinity) 38 return put(sink, "-inf"); 39 if(f == float.infinity) 40 return put(sink, "inf"); 41 if(f < 0) 42 { 43 f = -f; 44 put(sink, '-'); 45 } 46 47 float decimal = f - cast(int)f; 48 toStringRange(sink, cast(int)f); 49 if(decimal == 0) 50 return; 51 put(sink, '.'); 52 long multiplier = 10; 53 while(cast(long)(decimal*multiplier) < (decimal*multiplier)) 54 { 55 if(cast(long)(decimal*multiplier) == 0) 56 put(sink, '0'); 57 multiplier*=10; 58 } 59 toStringRange(sink, (cast(long)(decimal*multiplier))); 60 } 61 62 63 void toStringRange(Sink, T)(auto ref Sink sink, T[] arr) 64 if(isOutputRange!(Sink, char) && !is(T[] == string) && !is(T[] == char[])) //There is a better match for char/string 65 { 66 static if(__traits(compiles, sink.preAllocate)) 67 { 68 //2: '[' and ']' 69 // 2 * arr.length: ", " (no need to use - 1 as there will be the inputs) 70 sink.preAllocate(2 + 2 * arr.length); 71 } 72 put(sink, '['); 73 for(int i = 0; i < arr.length; i++) 74 { 75 if(i != 0) 76 { 77 foreach(character; ", ") 78 put(sink, character); 79 } 80 toStringRange(sink, arr[i]); 81 } 82 put(sink, ']'); 83 } 84 85 86 void toStringRange(Sink, T)(auto ref Sink sink, T structOrTupleOrClass) 87 if(!isArray!T && (is(T == struct) || is(T == class) || is(T == interface) || isTuple!T)) 88 { 89 static if(isTuple!T) 90 { 91 alias tupl = structOrTupleOrClass; 92 put(sink, T.stringof); 93 put(sink, '('); 94 foreach(i, v; tupl) 95 { 96 if(i > 0) 97 put(sink, ", "); 98 toStringRange(sink, v); 99 } 100 put(sink, ')'); 101 } 102 else static if(is(T == struct))//For structs declaration 103 { 104 import hip.util.reflection; 105 alias struct_ = structOrTupleOrClass; 106 static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format")) 107 { 108 import hip.util.format; 109 formatFromType(sink, struct_); 110 } 111 else 112 { 113 put(sink, T.stringof); 114 put(sink, '('); 115 foreach(i, v; struct_.tupleof) 116 { 117 if(i > 0) 118 put(sink, ", "); 119 toStringRange(sink, v); 120 } 121 put(sink, ')'); 122 } 123 } 124 else static if(is(T == class)) 125 { 126 alias class_ = structOrTupleOrClass; 127 put(sink, T.classinfo.name); 128 put(sink, '('); 129 foreach(i, v; class_.tupleof) 130 { 131 if(i > 0) 132 put(sink, ", "); 133 toStringRange(sink, v); 134 } 135 put(sink, ')'); 136 } 137 else static if(is(T == interface)) 138 { 139 put(sink, T.classinfo.name); 140 } 141 else static assert(0, "Not implemented for "~T.stringof); 142 } 143 144 // void toStringRange(Sink)(auto ref Sink sink, scope const char[] arr) if(isOutputRange!(Sink, char)) 145 // { 146 // static if(__traits(compiles, sink.preAllocate)) 147 // sink.preAllocate(arr.length); 148 // foreach(ch; arr) 149 // put(sink, ch); 150 // } 151 152 void toStringRange(Sink)(auto ref Sink sink, string str) if(isOutputRange!(Sink, char)) 153 { 154 static if(__traits(compiles, sink.preAllocate)) 155 sink.preAllocate(str.length); 156 foreach(character; str) 157 put(sink, character); 158 } 159 160 void toStringRange(Sink)(auto ref Sink sink, const(char)* str) if(isOutputRange!(Sink, char)) 161 { 162 import core.stdc.string:strlen; 163 size_t length = strlen(str); 164 static if(__traits(compiles, sink.preAllocate)) 165 sink.preAllocate(length); 166 for(int i = 0; i < length; i++) 167 put(sink, str[i]); 168 } 169 170 void toStringRange(Sink)(auto ref Sink sink, const(ubyte)* str) if(isOutputRange!(Sink, char)) 171 { 172 toStringRange(sink, cast(const(char)*)str); 173 } 174 175 void toStringRange(Sink)(auto ref Sink sink, void* ptr) if(isOutputRange!(Sink, char)) 176 { 177 if(ptr is null) 178 put(sink, "null"); 179 else 180 toHex(sink, cast(size_t)ptr); 181 } 182 183 void toStringRange(Sink)(auto ref Sink sink, bool b) if(isOutputRange!(Sink, char)) 184 { 185 put(sink, b ? "true" :"false"); 186 } 187 188 void toStringRange(Sink)(auto ref Sink sink, long x) 189 if(isOutputRange!(Sink, char)) 190 { 191 enum numbers = "0123456789"; 192 int preAllocSize = 0; 193 bool isNegative = x < 0; 194 if(isNegative) 195 { 196 x*= -1; 197 preAllocSize++; 198 } 199 ulong div = 10; 200 while(div <= x) 201 { 202 div*=10; 203 preAllocSize++; 204 } 205 div/= 10; 206 static if(__traits(hasMember, sink, "preAllocate")) 207 sink.preAllocate(preAllocSize); 208 if(isNegative) 209 put(sink, '-'); 210 while(div >= 10) 211 { 212 put(sink, numbers[(x/div)%10]); 213 div/=10; 214 } 215 put(sink, numbers[cast(size_t)(x%10)]); 216 } 217 218 219 void toHex(Sink)(auto ref Sink sink, size_t n) 220 if(isOutputRange!(Sink, char)) 221 { 222 enum numbers = "0123456789ABCDEF"; 223 int preAllocSize = 1; 224 ulong div = 16; 225 while(div <= n) 226 { 227 div*= 16; 228 preAllocSize++; 229 } 230 div/= 16; 231 static if(__traits(hasMember, sink, "preAllocate")) 232 sink.preAllocate(preAllocSize); 233 234 while(div >= 16) 235 { 236 put(sink, numbers[(n/div)%16]); 237 div/= 16; 238 } 239 put(sink, numbers[n%16]); 240 }